package scatter.wwd.rest.componentimpl.order;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import scatter.common.rest.exception.BusinessBadRequestException;
import scatter.common.rest.exception.BusinessException;
import scatter.common.rest.notify.NotifyParam;
import scatter.common.rest.notify.NotifyTool;
import scatter.dict.rest.service.IDictService;
import scatter.order.pojo.constant.OrderConstants;
import scatter.order.pojo.form.OrderCreateGoodsForm;
import scatter.order.pojo.param.CreateOrderParam;
import scatter.order.pojo.po.Order;
import scatter.order.pojo.po.OrderGoods;
import scatter.pay.rest.service.impl.DefaultThirdPayCreateOrderServiceImpl;
import scatter.wwd.pojo.form.app.ActivitySingupForm;
import scatter.wwd.pojo.po.ActivityEnrollRule;
import scatter.wwd.pojo.po.ActivityParticipate;
import scatter.wwd.pojo.po.UserInfo;
import scatter.wwd.rest.service.IActivityEnrollRuleService;
import scatter.wwd.rest.service.IActivityParticipateService;
import scatter.wwd.rest.service.IUserInfoService;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * <p>
 *
 * </p>
 *
 * @author yangwei
 * @since 2021-06-27 16:52
 */
@Service
@Slf4j
public class ActivityCreateOrderServiceImpl extends DefaultThirdPayCreateOrderServiceImpl {

	public static SegmentLock segmentLock = new SegmentLock(25,true);

	@Autowired
	private IActivityParticipateService iActivityParticipateService;

	@Autowired
	private IUserInfoService iUserInfoService;
	@Autowired
	private IActivityEnrollRuleService iActivityEnrollRuleService;
	@Autowired
	private IDictService iDictService;

	@Override
	protected boolean preCreateOrder(CreateOrderParam param) {
		// 校验
		OrderCreateGoodsForm orderCreateGoodsForm = param.getGoodsForms().get(0);
		String activityId = orderCreateGoodsForm.getId();
		ActivityEnrollRule byActivityId = iActivityEnrollRuleService.getByActivityId(activityId);
		if (byActivityId.getStartAt() != null) {
			if (LocalDateTimeUtil.now().isBefore(byActivityId.getStartAt())) {
				throw new BusinessException("活动报名时间尚未开始");
			}
		}
		if (byActivityId.getEndAt() != null) {
			if (LocalDateTimeUtil.now().isAfter(byActivityId.getEndAt())) {
				throw new BusinessException("活动报名时间已截止");
			}
		}
		// 是否已报名
		List<ActivityParticipate> byActivityIdAndUserIdAndIsQuit = iActivityParticipateService.getByActivityIdAndUserIdAndIsQuit(activityId, param.getUserId(), false);
		if (!isEmpty(byActivityIdAndUserIdAndIsQuit)) {
			throw new BusinessBadRequestException("您已报名该活动，请勿重复报名");

		}
		return super.preCreateOrder(param);
	}

	@Override
	protected boolean deductInventory(CreateOrderParam param) {
		Map<String, Object> ext = param.getExt();

		OrderCreateGoodsForm orderCreateGoodsForm = param.getGoodsForms().get(0);
		// 以活动id作为key 加锁
		String lockKey = orderCreateGoodsForm.getId();
				segmentLock.lock(lockKey);
		try {
			// 已报名人数加1
			iActivityEnrollRuleService.updateNum(orderCreateGoodsForm.getId(),param.getUserId(),1);

		} finally {
			segmentLock.unlock(lockKey);
		}
		return true;
	}

	@Override
	protected String existOrder(CreateOrderParam param) {
		// 这里只取一条数据，默认认为商品都是一种类型
		OrderCreateGoodsForm orderCreateGoodsForm = param.getGoodsForms().get(0);
		List<OrderGoods> byGoodsId = iOrderGoodsService.getByGoodsIdAndUserId(orderCreateGoodsForm.getId(),param.getUserId());
		if (!isEmpty(byGoodsId)) {

			Set<String> orderIds = byGoodsId.stream().map(OrderGoods::getOrderId).collect(Collectors.toSet());
			List<Order> orders = iOrderService.listByIds(orderIds);
			// 如果有待支付的就表示订单已存在
			for (Order order : orders) {
				if(isEqual(order.getStatusDictId(),dictService.getIdByGroupCodeAndValue(OrderConstants.OrderStatusGroupCode.order_status.groupCode(), OrderConstants.OrderStatusItem.wait_pay.itemValue()))){
					log.info("存在未支付的订单，订单已存在,orderNo={}",order.getOrderNo());
					if (LocalDateTimeUtil.now().isBefore(order.getExpireAt())) {
						log.info("存在未支付的订单，订单已存在,且未过期,不再创建新订单,orderNo={}",order.getOrderNo());
						return order.getOrderNo();
					}else {
						log.info("存在未支付的订单，订单已存在,但已过期,orderNo={}",order.getOrderNo());
					}

				}
			}
		}else {
			log.info("商品信息为空");
		}
		return null;
	}
	@Override
	public boolean support(String channel, String categoryDictValue) {
		return true;
	}

	@Override
	protected boolean isPostCreateOrderFailThrowException() {
		return true;
	}


	@Override
	protected void postCreateOrder(CreateOrderParam param, String orderNo) {
		super.postCreateOrder(param, orderNo);

		OrderCreateGoodsForm orderCreateGoodsForm = param.getGoodsForms().get(0);
		UserInfo byUserId = iUserInfoService.getByUserId(param.getUserId());
		Order byOrderNo = iOrderService.getByOrderNo(orderNo);


		ActivityParticipate activityParticipate = new ActivityParticipate();
		activityParticipate.setActivityId(orderCreateGoodsForm.getId());
		activityParticipate.setOrderId(byOrderNo.getId());
		activityParticipate.setOrderNo(orderNo);
		activityParticipate.setUserId(param.getUserId());
		// 线下支付，直接报名，线上支付默认quit = 1
		boolean isQuit = !OrderConstants.OrderPayTypeDictItem.off_line_pay.itemValue().equals(param.getPayTypeDictValue());
		activityParticipate.setIsQuit(isQuit);
		activityParticipate.setIsLeader(false);


		boolean save = iActivityParticipateService.save(activityParticipate);
		if (!save) {
			throw new BusinessException("报名失败，因保存活动参与返回false");
		}


		NotifyParam notifyParam = NotifyParam.business()
				.setTitle("活动报名下单通知")
				.setContent(StrUtil.format("用户 {} 下单活动 {}",byUserId.getNickname(),orderCreateGoodsForm.getName()));
		NotifyTool.notify(notifyParam);
	}

	/**
	 * 暂使用分段锁来实现并发控制
	 * @param <T>
	 */
	public static class SegmentLock<T> {

		/**
		 * 默认预先创建的锁数量.
		 */
		private static int DEFAULT_LOCK_COUNT = 20;

		private final ConcurrentHashMap<Integer, ReentrantLock> lockMap = new ConcurrentHashMap<>();

		public SegmentLock() {
			init(null, false);
		}

		public SegmentLock(Integer count, boolean isFair) {
			init(count, isFair);
		}

		private void init(Integer count, boolean isFair) {
			if (count != null && count != 0) {
				SegmentLock.DEFAULT_LOCK_COUNT = count;
			}
			// 预先初始化指定数量的锁
			for (int i = 0; i < SegmentLock.DEFAULT_LOCK_COUNT; i++) {
				this.lockMap.put(i, new ReentrantLock(isFair));
			}
		}

		public ReentrantLock get(T key) {
			return this.lockMap.get((key.hashCode() >>> 1) % DEFAULT_LOCK_COUNT);
		}

		public void lock(T key) {
			ReentrantLock lock = this.get(key);
			lock.lock();
		}

		public void unlock(T key) {
			ReentrantLock lock = this.get(key);
			lock.unlock();
		}
	}
}
